import type { VNodeChild } from 'vue'
import type { BaseComponentProps, Constructor, CustomSlots, RawProps } from './types'
import type { NvOption } from '@/types'
import { NDatePicker, NInput, NInputNumber, NSwitch, NTimePicker } from 'naive-ui'
import Cascader from '@/custom-input/Cascader.vue'
import Checkbox from '@/custom-input/Checkbox.vue'
import Radio from '@/custom-input/Radio.vue'
import Select from '@/custom-input/Select.vue'
import TreeSelect from '@/custom-input/TreeSelect.vue'
import Upload from '@/custom-input/Upload.vue'
import { renderDateView, renderDictView, renderSwitchView, renderTimeView, renderUploadView } from './components/render-view'

export interface RenderViewProps {
  value?: any
  dictData?: NvOption[]
  mode?: 'view' | 'cell'
  [k: string]: any
}
export type RenderView = ((props: RenderViewProps) => VNodeChild) | Constructor<RenderViewProps>

interface ComponentConfig<P> {
  component: Constructor<P>
  defaultProps?: ((RawProps & P) | ({} extends P ? null : never)) & BaseComponentProps
  defaultSlots?: CustomSlots
  config?: {
    /**
     * 组件双向绑定的属性
     * @default 'value'
     */
    vModel?: string
    /**
     * 自定义渲染查看
     */
    renderView?: RenderView
  }
}

class NvCrudContext {
  private readonly instace: Record<string, ComponentConfig<any>> = {}

  register<P>(
    name: string,
    component: ComponentConfig<P>['component'],
    defaultProps?: ComponentConfig<P>['defaultProps'],
    defaultSlots?: ComponentConfig<P>['defaultSlots'],
    config?: ComponentConfig<P>['config'],
  ) {
    if (name in this.instace) {
      console.warn(`[nv-crud]: Component '${name}' has already been registered. The new component will overwrite the existing one.`)
    }

    this.instace[name] = { component, defaultProps, defaultSlots, config }
  }

  getComponent(name: string, defaultName?: string): ComponentConfig<any> {
    const config = this.instace[name]
    if (!config) {
      console.error(`[nv-crud]: Component '${name}' is not registered.`)
      if (defaultName) {
        return this.getComponent(defaultName)
      }
    }
    return config
  }
}

export const NV_CRUD_CONTEXT = new NvCrudContext()

NV_CRUD_CONTEXT.register('input', NInput, { type: 'text' })
NV_CRUD_CONTEXT.register('textarea', NInput, { type: 'textarea' })
NV_CRUD_CONTEXT.register('password', NInput, { type: 'password' })
NV_CRUD_CONTEXT.register('number', NInputNumber)
NV_CRUD_CONTEXT.register('switch', NSwitch, undefined, undefined, { renderView: renderSwitchView })
NV_CRUD_CONTEXT.register('radio', Radio, undefined, undefined, { renderView: renderDictView })
NV_CRUD_CONTEXT.register('checkbox', Checkbox, undefined, undefined, { renderView: renderDictView })
NV_CRUD_CONTEXT.register('select', Select, undefined, undefined, { renderView: renderDictView })
NV_CRUD_CONTEXT.register('tree-select', TreeSelect, undefined, undefined, { renderView: renderDictView })
NV_CRUD_CONTEXT.register('cascader', Cascader, undefined, undefined, { renderView: renderDictView })
NV_CRUD_CONTEXT.register('upload', Upload, undefined, undefined, { vModel: 'fileList', renderView: renderUploadView })
NV_CRUD_CONTEXT.register('datetime', NDatePicker, { type: 'datetime' }, undefined, { vModel: 'formattedValue', renderView: renderDateView })
NV_CRUD_CONTEXT.register('date', NDatePicker, { type: 'date' }, undefined, { vModel: 'formattedValue', renderView: renderDateView })
NV_CRUD_CONTEXT.register('month', NDatePicker, { type: 'month' }, undefined, { vModel: 'formattedValue', renderView: renderDateView })
NV_CRUD_CONTEXT.register('year', NDatePicker, { type: 'year' }, undefined, { vModel: 'formattedValue', renderView: renderDateView })
NV_CRUD_CONTEXT.register('quarter', NDatePicker, { type: 'quarter', format: 'yyyy-\'Q\'Q' }, undefined, { vModel: 'formattedValue', renderView: renderDateView })
NV_CRUD_CONTEXT.register('week', NDatePicker, { type: 'week' }, undefined, { vModel: 'formattedValue', renderView: renderDateView })
NV_CRUD_CONTEXT.register('time', NTimePicker, undefined, undefined, { vModel: 'formattedValue', renderView: renderTimeView })
NV_CRUD_CONTEXT.register('daterange', NDatePicker, { type: 'daterange' }, undefined, { vModel: 'formattedValue', renderView: renderDateView })
NV_CRUD_CONTEXT.register('datetimerange', NDatePicker, { type: 'datetimerange' }, undefined, { vModel: 'formattedValue', renderView: renderDateView })
NV_CRUD_CONTEXT.register('monthrange', NDatePicker, { type: 'monthrange' }, undefined, { vModel: 'formattedValue', renderView: renderDateView })
NV_CRUD_CONTEXT.register('yearrange', NDatePicker, { type: 'yearrange' }, undefined, { vModel: 'formattedValue', renderView: renderDateView })
NV_CRUD_CONTEXT.register('quarterrange', NDatePicker, { type: 'quarterrange', format: 'yyyy-\'Q\'Q' }, undefined, { vModel: 'formattedValue', renderView: renderDateView })
